#include <cstring>
#include <string>

using namespace std;

/************************************************************************/
/*                        UtilsFindFilenameStart()                        */
/************************************************************************/

static int UtilsFindFilenameStart( const char * pszFilename )

{
    size_t iFileStart = strlen(pszFilename);

    for( ;
         iFileStart > 0
             && pszFilename[iFileStart-1] != '/'
             && pszFilename[iFileStart-1] != '\\';
         iFileStart-- ) {}

    return static_cast<int>( iFileStart );
}


/************************************************************************/
/*                             UtilsGetPath()                             */
/************************************************************************/

/**
 * Extract directory path portion of filename.
 *
 * Returns a string containing the directory path portion of the passed
 * filename.  If there is no path in the passed filename an empty string
 * will be returned (not NULL).
 *
 * <pre>
 * UtilsGetPath( "abc/def.xyz" ) == "abc"
 * UtilsGetPath( "/abc/def/" ) == "/abc/def"
 * UtilsGetPath( "/" ) == "/"
 * UtilsGetPath( "/abc/def" ) == "/abc"
 * UtilsGetPath( "abc" ) == ""
 * </pre>
 *
 * @param pszFilename the filename potentially including a path.
 *
 *  @return Path in an internal string which must not be freed.  The string
 * may be destroyed by the next CPL filename handling call.  The returned
 * will generally not contain a trailing path separator.
 */
string UtilsGetPath( const char *pszFilename )
{
    const int iFileStart = UtilsFindFilenameStart(pszFilename);
    if (iFileStart == 0) return "";
    return string(pszFilename, pszFilename+iFileStart-1);
}


/************************************************************************/
/*                             UtilsGetDirname()                          */
/************************************************************************/

/**
 * Extract directory path portion of filename.
 *
 * Returns a string containing the directory path portion of the passed
 * filename.  If there is no path in the passed filename the dot will be
 * returned.  It is the only difference from UtilsGetPath().
 *
 * <pre>
 * UtilsGetDirname( "abc/def.xyz" ) == "abc"
 * UtilsGetDirname( "/abc/def/" ) == "/abc/def"
 * UtilsGetDirname( "/" ) == "/"
 * UtilsGetDirname( "/abc/def" ) == "/abc"
 * UtilsGetDirname( "abc" ) == "."
 * </pre>
 *
 * @param pszFilename the filename potentially including a path.
 *
 * @return Path in an internal string which must not be freed.  The string
 * may be destroyed by the next CPL filename handling call.  The returned
 * will generally not contain a trailing path separator.
 */
string UtilsGetDirname( const char *pszFilename )
{
    const int iFileStart = UtilsFindFilenameStart(pszFilename);
    if (iFileStart == 0) return ".";
    return string(pszFilename, pszFilename+iFileStart-1);
}


/************************************************************************/
/*                           UtilsGetFilename()                           */
/************************************************************************/

/**
 * Extract non-directory portion of filename.
 *
 * Returns a string containing the bare filename portion of the passed
 * filename.  If there is no filename (passed value ends in trailing directory
 * separator) an empty string is returned.
 *
 * <pre>
 * UtilsGetFilename( "abc/def.xyz" ) == "def.xyz"
 * UtilsGetFilename( "/abc/def/" ) == ""
 * UtilsGetFilename( "abc/def" ) == "def"
 * </pre>
 *
 * @param pszFullFilename the full filename potentially including a path.
 *
 * @return just the non-directory portion of the path (points back into
 * original string).
 */

string UtilsGetFilename( const char *pszFullFilename )
{
    const int iLen = static_cast<int>(strlen(pszFullFilename));
    const int iFileStart = UtilsFindFilenameStart( pszFullFilename );
    return string(pszFullFilename+iFileStart, pszFullFilename+iLen);
}

/************************************************************************/
/*                           UtilsGetBasename()                           */
/************************************************************************/

/**
 * Extract basename (non-directory, non-extension) portion of filename.
 *
 * Returns a string containing the file basename portion of the passed
 * name.  If there is no basename (passed value ends in trailing directory
 * separator, or filename starts with a dot) an empty string is returned.
 *
 * <pre>
 * UtilsGetBasename( "abc/def.xyz" ) == "def"
 * UtilsGetBasename( "abc/def" ) == "def"
 * UtilsGetBasename( "abc/def/" ) == ""
 * </pre>
 *
 * @param pszFullFilename the full filename potentially including a path.
 *
 * @return just the non-directory, non-extension portion of the path in
 * an internal string which must not be freed.  The string
 * may be destroyed by the next CPL filename handling call.
 */

string UtilsGetBasename( const char *pszFullFilename )
{
    const size_t iFileStart = static_cast<size_t>( UtilsFindFilenameStart( pszFullFilename ) );
    size_t iExtStart = strlen(pszFullFilename);
    for (; 
         iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
         iExtStart-- ) {}
    if (iExtStart == iFileStart) iExtStart = strlen(pszFullFilename);
    return string(pszFullFilename+static_cast<int>(iFileStart), 
                  pszFullFilename+static_cast<int>(iExtStart));
}


/************************************************************************/
/*                           UtilsGetExtension()                          */
/************************************************************************/

/**
 * Extract filename extension from full filename.
 *
 * Returns a string containing the extension portion of the passed
 * name.  If there is no extension (the filename has no dot) an empty string
 * is returned.  The returned extension will not include the period.
 *
 * <pre>
 * UtilsGetExtension( "abc/def.xyz" ) == "xyz"
 * UtilsGetExtension( "abc/def" ) == ""
 * </pre>
 *
 * @param pszFullFilename the full filename potentially including a path.
 *
 * @return just the extension portion of the path in
 * an internal string which must not be freed.  The string
 * may be destroyed by the next CPL filename handling call.
 */
string UtilsGetExtension( const char *pszFullFilename )
{
    if (pszFullFilename[0] == '\0') return "";
    size_t iFileStart = static_cast<size_t>( UtilsFindFilenameStart( pszFullFilename ) );
    size_t sLen = strlen(pszFullFilename);
    size_t iExtStart = strlen(pszFullFilename);
    for ( ;
         iExtStart > iFileStart && pszFullFilename[iExtStart] != '.';
         iExtStart-- ) {}
    if (iExtStart == iFileStart)
        return "";
    // If the extension is too long, it is very much likely not an extension,
    // but another component of the path
    const size_t knMaxExtensionSize = 10;
    if (sLen - iExtStart > knMaxExtensionSize) {
        return "";
    }
    return string(iExtStart+1+pszFullFilename, pszFullFilename+sLen);
}

